#include "sdk.h"
#include <wx/stdpaths.h>
#include <cstdlib>
#include "scripting/sqplus/sqplus.h"
#include "scripting/bindings/scriptbindings.h"
#include "M_Code_StyledTextCtrl.h"

using namespace std;

template<> MacrosManager* Mgr<MacrosManager>::instance = nullptr;
template<> bool  Mgr<MacrosManager>::isShutdown = false;

static const wxString const_COIN( _T( "COIN" ) );
static const wxString const_RANDOM( _T( "RANDOM" ) );
static const wxString toNativePath( _T( "$TO_NATIVE_PATH{" ) );
static const wxString toUnixPath( _T( "$TO_UNIX_PATH{" ) );
static const wxString toWindowsPath( _T( "$TO_WINDOWS_PATH{" ) );

MacrosManager::MacrosManager() {
  Reset();
}

MacrosManager::~MacrosManager() {
}

void MacrosManager::CreateMenu( M_Code_unused wxMenuBar* menuBar ) {
}

void MacrosManager::ReleaseMenu( M_Code_unused wxMenuBar* menuBar ) {
}

wxString MacrosManager::ReplaceMacros( const wxString& buffer, ProjectBuildTarget* target ) {
  wxString tmp( buffer );
  ReplaceMacros( tmp, target );
  return tmp;
}

void MacrosManager::Reset() {
  m_LastProject = nullptr;
  m_LastTarget = nullptr;
  m_ActiveEditorFilename = wxEmptyString;
  m_ActiveEditorLine = -1;
  m_ActiveEditorColumn = -1;
  m_AppPath = UnixFilename( ConfigManager::GetExecutableFolder() );
  m_Plugins = UnixFilename( ConfigManager::GetPluginsFolder() );
  m_DataPath = UnixFilename( ConfigManager::GetDataFolder() );
  ClearProjectKeys();
  m_RE_Unix.Compile( _T( "([^$]|^)(\\$[({]?(#?[A-Za-z_0-9.]+)[)} /\\]?)" ), wxRE_EXTENDED | wxRE_NEWLINE );
  m_RE_DOS.Compile( _T( "([^%]|^)(%(#?[A-Za-z_0-9.]+)%)" ), wxRE_EXTENDED | wxRE_NEWLINE );
  m_RE_If.Compile( _T( "\\$if\\(([^)]*)\\)[::space::]*(\\{([^}]*)\\})(\\{([^}]*)\\})?" ), wxRE_EXTENDED | wxRE_NEWLINE );
  m_RE_IfSp.Compile( _T( "[^=!<>]+|(([^=!<>]+)[ ]*(=|==|!=|>|<|>=|<=)[ ]*([^=!<>]+))" ), wxRE_EXTENDED | wxRE_NEWLINE );
  m_RE_Script.Compile( _T( "(\\[\\[(.*)\\]\\])" ), wxRE_EXTENDED | wxRE_NEWLINE );
  m_RE_ToAbsolutePath.Compile( _T( "\\$TO_ABSOLUTE_PATH{([^}]*)}" ), wxRE_ADVANCED );
  m_RE_To83Path.Compile( _T( "\\$TO_83_PATH{([^}]*)}" ), wxRE_ADVANCED );
  m_RE_RemoveQuotes.Compile( _T( "\\$REMOVE_QUOTES{([^}]*)}" ), wxRE_ADVANCED );
  m_UserVarMan = Manager::Get()->GetUserVariableManager();
  srand( time( nullptr ) );
  assert( m_RE_Unix.IsValid() );
  assert( m_RE_DOS.IsValid() );
}

void MacrosManager::ClearProjectKeys() {
  m_Macros.clear();
  m_Macros[_T( "AMP" )] = _T( "&" );
  m_Macros[_T( "CODEBLOCKS" )] = m_AppPath;
  m_Macros[_T( "APP_PATH" )] = m_AppPath;
  m_Macros[_T( "APP-PATH" )] = m_AppPath;
  m_Macros[_T( "APPPATH" )] = m_AppPath;
  m_Macros[_T( "DATA_PATH" )] = m_DataPath;
  m_Macros[_T( "DATA-PATH" )] = m_DataPath;
  m_Macros[_T( "DATAPATH" )] = m_DataPath;
  m_Macros[_T( "PLUGINS" )] = m_Plugins;
  m_Macros[_T( "LANGUAGE" )] = wxLocale::GetLanguageName( wxLocale::GetSystemLanguage() );
  m_Macros[_T( "ENCODING" )] = wxLocale::GetSystemEncodingName();
  m_Macros[_T( "CMD_CP" )] = _T( "cp --preserve=timestamps" );
  m_Macros[_T( "CMD_RM" )] = _T( "rm" );
  m_Macros[_T( "CMD_MV" )] = _T( "mv" );
  m_Macros[_T( "CMD_NULL" )] = _T( "/dev/null" );
  m_Macros[_T( "CMD_MKDIR" )] = _T( "mkdir -p" );
  m_Macros[_T( "CMD_RMDIR" )] = _T( "rmdir" );
  M_Code_Workspace* wksp = Manager::Get()->GetProjectManager()->GetWorkspace();
  if( wksp ) {
    m_WorkspaceWxFileName.Assign( wksp->GetFilename() );
    m_WorkspaceFilename = UnixFilename( m_WorkspaceWxFileName.GetFullName() );
    m_WorkspaceName = wksp->GetTitle();
    m_WorkspaceDir = UnixFilename( m_WorkspaceWxFileName.GetPath() );
  } else {
    m_WorkspaceWxFileName.Clear();
    m_WorkspaceFilename = wxEmptyString;
    m_WorkspaceName = wxEmptyString;
    m_WorkspaceDir = wxEmptyString;
  }
  m_Macros[_T( "WORKSPACE_FILE" )] = m_WorkspaceFilename;
  m_Macros[_T( "WORKSPACE_FILENAME" )] = m_WorkspaceFilename;
  m_Macros[_T( "WORKSPACE_FILE_NAME" )] = m_WorkspaceFilename;
  m_Macros[_T( "WORKSPACEFILE" )] = m_WorkspaceFilename;
  m_Macros[_T( "WORKSPACEFILENAME" )] = m_WorkspaceFilename;
  m_Macros[_T( "WORKSPACENAME" )] = m_WorkspaceName;
  m_Macros[_T( "WORKSPACE_NAME" )] = m_WorkspaceName;
  m_Macros[_T( "WORKSPACE_DIR" )] = m_WorkspaceDir;
  m_Macros[_T( "WORKSPACE_DIRECTORY" )] = m_WorkspaceDir;
  m_Macros[_T( "WORKSPACEDIR" )] = m_WorkspaceDir;
  m_Macros[_T( "WORKSPACEDIRECTORY" )] = m_WorkspaceDir;
}

wxString GetSelectedText() {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    M_Code_StyledTextCtrl* stc = ed->GetControl();
    if( stc ) {
      wxString text = stc->GetSelectedText();
      if( text.size() ) {
        return text;
      } else {
        int iCurrentPos = stc->GetCurrentPos();
        return stc->GetTextRange( stc->WordStartPosition( iCurrentPos, true ), stc->WordEndPosition( iCurrentPos, true ) );
      }
    }
  }
  return wxEmptyString;
}

namespace {
  template<typename T>
  void ReadMacros( MacrosMap &macros, T *object ) {
    if( object ) {
      const StringHash& v = object->GetAllVars();
      for( StringHash::const_iterator it = v.begin(); it != v.end(); ++it ) {
        macros[it->first.Upper()] = it->second;
      }
    }
  }
}

void MacrosManager::RecalcVars( M_Code_Project* project, EditorBase* editor, ProjectBuildTarget* target ) {
  m_ActiveEditorFilename = wxEmptyString;
  m_ActiveEditorLine = -1;
  m_ActiveEditorColumn = -1;
  if( editor ) {
    if( editor->GetFilename() != m_ActiveEditorFilename ) {
      m_ActiveEditorFilename = editor->GetFilename();
    }
    if( editor->IsBuiltinEditor() ) {
      M_Code_Editor* cbEd  = static_cast<M_Code_Editor*>( editor );
      M_Code_StyledTextCtrl* cbSTC = cbEd->GetControl();
      if( cbSTC ) {
        m_ActiveEditorLine = cbSTC->GetCurrentLine() + 1;
        int pos = cbSTC->GetCurrentPos();
        if( pos != -1 ) {
          m_ActiveEditorColumn = cbSTC->GetColumn( pos ) + 1;
        }
      }
    }
  }
  if( !project ) {
    m_ProjectFilename = wxEmptyString;
    m_ProjectName = wxEmptyString;
    m_ProjectDir = wxEmptyString;
    m_ProjectFiles = wxEmptyString;
    m_Makefile = wxEmptyString;
    m_LastProject = nullptr;
    ClearProjectKeys();
    m_Macros[_T( "PROJECTFILE" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_FILE" )] = wxEmptyString;
    m_Macros[_T( "PROJECTFILENAME" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_FILENAME" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_FILE_NAME" )] = wxEmptyString;
    m_Macros[_T( "PROJECTNAME" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_NAME" )] = wxEmptyString;
    m_Macros[_T( "PROJECTDIR" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_DIR" )] = wxEmptyString;
    m_Macros[_T( "PROJECTDIRECTORY" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_DIRECTORY" )] = wxEmptyString;
    m_Macros[_T( "PROJECTTOPDIR" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_TOPDIR" )] = wxEmptyString;
    m_Macros[_T( "PROJECTTOPDIRECTORY" )] = wxEmptyString;
    m_Macros[_T( "PROJECT_TOPDIRECTORY" )] = wxEmptyString;
    m_Macros[_T( "MAKEFILE" )] = wxEmptyString;
    m_Macros[_T( "ALL_PROJECT_FILES" )] = wxEmptyString;
  } else if( ( project != m_LastProject ) || ( project->GetTitle() != m_ProjectName ) || ( UnixFilename( project->GetBasePath() ) != m_ProjectDir )
             || ( UnixFilename( m_ProjectWxFileName.GetFullName() ) != m_ProjectFilename ) ) {
    m_LastTarget = nullptr;
    m_ProjectWxFileName.Assign( project->GetFilename() );
    m_ProjectFilename = UnixFilename( m_ProjectWxFileName.GetFullName() );
    m_ProjectName = project->GetTitle();
    m_ProjectDir = UnixFilename( project->GetBasePath() );
    m_ProjectTopDir = UnixFilename( project->GetCommonTopLevelPath() );
    m_Makefile = UnixFilename( project->GetMakefile() );
    m_ProjectFiles = wxEmptyString;
    for( FilesList::iterator it = project->GetFilesList().begin(); it != project->GetFilesList().end(); ++it ) {
      wxString out = UnixFilename( ( ( ProjectFile* )*it )->relativeFilename );
      QuoteStringIfNeeded( out );
      m_ProjectFiles << out << _T( ' ' );
    }
    ClearProjectKeys();
    m_Macros[_T( "PROJECTFILE" )] = m_ProjectFilename;
    m_Macros[_T( "PROJECT_FILE" )] = m_ProjectFilename;
    m_Macros[_T( "PROJECTFILENAME" )] = m_ProjectFilename;
    m_Macros[_T( "PROJECT_FILENAME" )] = m_ProjectFilename;
    m_Macros[_T( "PROJECT_FILE_NAME" )] = m_ProjectFilename;
    m_Macros[_T( "PROJECTNAME" )] = m_ProjectName;
    m_Macros[_T( "PROJECT_NAME" )] = m_ProjectName;
    m_Macros[_T( "PROJECTDIR" )] = m_ProjectDir;
    m_Macros[_T( "PROJECT_DIR" )] = m_ProjectDir;
    m_Macros[_T( "PROJECTDIRECTORY" )] = m_ProjectDir;
    m_Macros[_T( "PROJECT_DIRECTORY" )] = m_ProjectDir;
    m_Macros[_T( "PROJECTTOPDIR" )] = m_ProjectTopDir;
    m_Macros[_T( "PROJECT_TOPDIR" )] = m_ProjectTopDir;
    m_Macros[_T( "PROJECTTOPDIRECTORY" )] = m_ProjectTopDir;
    m_Macros[_T( "PROJECT_TOPDIRECTORY" )] = m_ProjectTopDir;
    m_Macros[_T( "MAKEFILE" )] = m_Makefile;
    m_Macros[_T( "ALL_PROJECT_FILES" )] = m_ProjectFiles;
    for( int i = 0; i < project->GetBuildTargetsCount(); ++i ) {
      ProjectBuildTarget* it_target = project->GetBuildTarget( i );
      if( !it_target ) {
        continue;
      }
      wxString title = it_target->GetTitle().Upper();
      while( title.Replace( _T( " " ), _T( "_" ) ) ) ;
      m_Macros[title + _T( "_OUTPUT_FILE" )] = UnixFilename( it_target->GetOutputFilename() );
      m_Macros[title + _T( "_OUTPUT_DIR" )] = UnixFilename( it_target->GetBasePath() );
      m_Macros[title + _T( "_OUTPUT_BASENAME" )] = wxFileName( it_target->GetOutputFilename() ).GetName();
      m_Macros[title + _T( "_PARAMETERS" )] = it_target->GetExecutionParameters();
    }
    m_LastProject = project;
  }
  if( target ) {
    const Compiler* compiler = CompilerFactory::GetCompiler( target->GetCompilerID() );
    ReadMacros( m_Macros, compiler );
  }
  ReadMacros( m_Macros, project );
  if( !target ) {
    m_TargetOutputDir = wxEmptyString;
    m_TargetName = wxEmptyString;
    m_TargetOutputBaseName = wxEmptyString;
    m_TargetOutputFilename = wxEmptyString;
    m_TargetFilename = wxEmptyString;
    m_LastTarget = nullptr;
  } else if( ( target != m_LastTarget ) or ( target->GetTitle() != m_TargetName ) ) {
    wxFileName tod( target->GetOutputFilename() );
    m_TargetOutputDir = UnixFilename( tod.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR ) );
    m_TargetName = UnixFilename( target->GetTitle() );
    m_TargetOutputBaseName = wxFileName( target->GetOutputFilename() ).GetName();
    m_TargetOutputFilename = wxFileName( target->GetOutputFilename() ).GetFullName();
    m_TargetFilename = UnixFilename( target->GetOutputFilename() );
    m_LastTarget = target;
  }
  if( target ) {
    ReadMacros( m_Macros, target );
    if( Compiler* c = CompilerFactory::GetCompiler( target->GetCompilerID() ) ) {
      m_Macros[_T( "TARGET_CC" )] = c->GetPrograms().C;
      m_Macros[_T( "TARGET_CPP" )] = c->GetPrograms().CPP;
      m_Macros[_T( "TARGET_LD" )] = c->GetPrograms().LD;
      m_Macros[_T( "TARGET_LIB" )] = c->GetPrograms().LIB;
      wxFileName MasterPath;
      MasterPath.SetPath( c->GetMasterPath(), wxPATH_NATIVE );
      m_Macros[_T( "TARGET_COMPILER_DIR" )] = MasterPath.GetPathWithSep( wxPATH_NATIVE );
    }
    m_Macros[_T( "TARGET_OBJECT_DIR" )] = target->GetObjectOutput();
  }
  m_Macros[_T( "TARGET_OUTPUT_DIR" )] = m_TargetOutputDir;
  m_Macros[_T( "TARGET_NAME" )] = m_TargetName;
  m_Macros[_T( "TARGET_OUTPUT_BASENAME" )] = m_TargetOutputBaseName;
  m_Macros[_T( "TARGET_OUTPUT_FILENAME" )] = m_TargetOutputFilename;
  m_Macros[_T( "TARGET_OUTPUT_FILE" )] = m_TargetFilename;
  m_Macros[_T( "ACTIVE_EDITOR_FILENAME" )] = UnixFilename( m_ActiveEditorFilename );
  wxFileName fn( m_Macros[_T( "ACTIVE_EDITOR_FILENAME" )] );
  m_Macros[_T( "ACTIVE_EDITOR_DIRNAME" )] = fn.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
  m_Macros[_T( "ACTIVE_EDITOR_STEM" )] = fn.GetName();
  m_Macros[_T( "ACTIVE_EDITOR_EXT" )] = fn.GetExt();
  wxString temp_str;
  temp_str.Printf( _T( "%d" ), m_ActiveEditorLine );
  m_Macros[_T( "ACTIVE_EDITOR_LINE" )] = temp_str;
  temp_str.Printf( _T( "%d" ), ( m_ActiveEditorLine > 0 ) ? m_ActiveEditorLine - 1 : -1 );
  m_Macros[_T( "ACTIVE_EDITOR_LINE_0" )] = temp_str;
  temp_str.Printf( _T( "%d" ), m_ActiveEditorColumn );
  m_Macros[_T( "ACTIVE_EDITOR_COLUMN" )] = temp_str;
  temp_str.Printf( _T( "%d" ), ( m_ActiveEditorColumn > 0 ) ? m_ActiveEditorColumn - 1 : -1 );
  m_Macros[_T( "ACTIVE_EDITOR_COLUMN_0" )] = temp_str;
  m_Macros[_T( "ACTIVE_EDITOR_SELECTION" )] = GetSelectedText();
  m_Macros[_T( "GET_DATA_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetDataDir();
  m_Macros[_T( "GET_LOCAL_DATA_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetLocalDataDir();
  m_Macros[_T( "GET_DOCUMENTS_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetDocumentsDir();
  m_Macros[_T( "GET_CONFIG_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetConfigDir();
  m_Macros[_T( "GET_USER_CONFIG_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetUserConfigDir();
  m_Macros[_T( "GET_USER_DATA_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetUserDataDir();
  m_Macros[_T( "GET_USER_LOCAL_DATA_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetUserLocalDataDir();
  m_Macros[_T( "GET_TEMP_DIR" )] = ( ( const wxStandardPaths& )wxStandardPaths::Get() ).GetTempDir();
  wxDateTime now( wxDateTime::Now() );
  m_Macros[_T( "TDAY" )] = now.Format( _T( "%Y%m%d" ) );
  m_Macros[_T( "TODAY" )] = now.Format( _T( "%Y-%m-%d" ) );
  m_Macros[_T( "NOW" )] = now.Format( _T( "%Y-%m-%d-%H.%M" ) );
  m_Macros[_T( "NOW_L" )] = now.Format( _T( "%Y-%m-%d-%H.%M.%S" ) );
  m_Macros[_T( "WEEKDAY" )] = now.Format( _T( "%A" ) );
  wxDateTime nowGMT( now.ToGMT() );
  m_Macros[_T( "TDAY_UTC" )] = nowGMT.Format( _T( "%Y%m%d" ) );
  m_Macros[_T( "TODAY_UTC" )] = nowGMT.Format( _T( "%Y-%m-%d" ) );
  m_Macros[_T( "NOW_UTC" )] = nowGMT.Format( _T( "%Y-%m-%d-%H.%M" ) );
  m_Macros[_T( "NOW_L_UTC" )] = nowGMT.Format( _T( "%Y-%m-%d-%H.%M.%S" ) );
  m_Macros[_T( "WEEKDAY_UTC" )] = nowGMT.Format( _T( "%A" ) );
  wxDateTime january_1_2010( 1, wxDateTime::Jan, 2010, 0, 0, 0, 0 );
  wxTimeSpan ts = now.Subtract( january_1_2010 );
  m_Macros[_T( "DAYCOUNT" )] = F( _T( "%d" ), ts.GetDays() );
}

void MacrosManager::ReplaceMacros( wxString& buffer, ProjectBuildTarget* target, bool subrequest ) {
  if( buffer.IsEmpty() ) {
    return;
  }
  static const wxString delim( _T( "$%[" ) );
  if( buffer.find_first_of( delim ) == wxString::npos ) {
    return;
  }
  M_Code_Project* project = target ? target->GetParentProject()
                            : Manager::Get()->GetProjectManager()->GetActiveProject();
  EditorBase* editor = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( !target ) {
    if( project ) {
      target = project->GetCurrentlyCompilingTarget();
      if( !target ) {
        target = project->GetBuildTarget( project->GetActiveBuildTarget() );
      }
    }
  }
  if( project != m_LastProject || target != m_LastTarget || ( editor && ( editor->GetFilename() != m_ActiveEditorFilename ) )
      || ( project && ( UnixFilename( project->GetBasePath() ) != m_ProjectDir ) )
      || ( UnixFilename( m_ProjectWxFileName.GetFullName() ) != m_ProjectFilename )
      || ( target && ( target->GetTitle() != m_TargetName ) ) ) {
    RecalcVars( project, editor, target );
  }
  wxString search;
  wxString replace;
  if( buffer.find( _T( "$if" ) ) != wxString::npos ) {
    while( m_RE_If.Matches( buffer ) ) {
      search = m_RE_If.GetMatch( buffer, 0 );
      replace = EvalCondition( m_RE_If.GetMatch( buffer, 1 ), m_RE_If.GetMatch( buffer, 3 ), m_RE_If.GetMatch( buffer, 5 ), target );
      buffer.Replace( search, replace, false );
    }
  }
  while( m_RE_Script.Matches( buffer ) ) {
    search = m_RE_Script.GetMatch( buffer, 1 );
    replace = Manager::Get()->GetScriptingManager()->LoadBufferRedirectOutput( m_RE_Script.GetMatch( buffer, 2 ) );
    buffer.Replace( search, replace, false );
  }
  while( m_RE_ToAbsolutePath.Matches( buffer ) ) {
    search = m_RE_ToAbsolutePath.GetMatch( buffer, 0 );
    const wxString relativePath = m_RE_ToAbsolutePath.GetMatch( buffer, 1 );
    wxFileName fn( relativePath );
    fn.MakeAbsolute();
    replace = fn.GetFullPath();
    buffer.Replace( search, replace, false );
  }
  while( m_RE_To83Path.Matches( buffer ) ) {
    search = m_RE_To83Path.GetMatch( buffer, 0 );
    const wxString path = m_RE_To83Path.GetMatch( buffer, 1 );
    wxFileName fn( path );
    fn.MakeAbsolute();
    replace = fn.GetShortPath();
    buffer.Replace( search, replace, false );
  }
  int index = wxNOT_FOUND;
  while( ( index = buffer.Index( toNativePath ) ) != wxNOT_FOUND ) {
    int end = MatchBrace( buffer, index + toNativePath.Length() - 1 );
    wxString content = buffer.Mid( index + toNativePath.Length(), end - index - toNativePath.Length() );
    ReplaceMacros( content, target, true );
    buffer.Replace( buffer.Mid( index, end - index + 1 ), UnixFilename( content ), false );
  }
  while( ( index = buffer.Index( toUnixPath ) ) != wxNOT_FOUND ) {
    int end = MatchBrace( buffer, index + toUnixPath.Length() - 1 );
    wxString content = buffer.Mid( index + toUnixPath.Length(), end - index - toUnixPath.Length() );
    ReplaceMacros( content, target, true );
    buffer.Replace( buffer.Mid( index, end - index + 1 ), UnixFilename( content, wxPATH_UNIX ), false );
  }
  while( ( index = buffer.Index( toWindowsPath ) ) != wxNOT_FOUND ) {
    int end = MatchBrace( buffer, index + toWindowsPath.Length() - 1 );
    wxString content = buffer.Mid( index + toWindowsPath.Length(), end - index - toWindowsPath.Length() );
    ReplaceMacros( content, target, true );
    buffer.Replace( buffer.Mid( index, end - index + 1 ), UnixFilename( content, wxPATH_WIN ), false );
  }
  while( m_RE_RemoveQuotes.Matches( buffer ) ) {
    search = m_RE_RemoveQuotes.GetMatch( buffer, 0 );
    wxString content = m_RE_RemoveQuotes.GetMatch( buffer, 1 ).Trim().Trim( false );
    if( content.StartsWith( _T( "$" ) ) ) {
      ReplaceMacros( content, target, subrequest );
    }
    if( content.Len() > 2 && content.StartsWith( _T( "\"" ) ) && content.EndsWith( _T( "\"" ) ) ) {
      replace = content.Mid( 1, content.Len() - 2 );
      buffer.Replace( search, replace, false );
    } else
    { buffer.Replace( search, content, false ); }
  }
  while( m_RE_Unix.Matches( buffer ) ) {
    replace.Empty();
    search = m_RE_Unix.GetMatch( buffer, 2 );
    wxString var = m_RE_Unix.GetMatch( buffer, 3 ).Upper();
    if( var.GetChar( 0 ) == _T( '#' ) ) {
      replace = UnixFilename( m_UserVarMan->Replace( var ) );
    } else {
      if( var.compare( const_COIN ) == 0 ) {
        replace.assign( 1u, rand() & 1 ? _T( '1' ) : _T( '0' ) );
      } else if( var.compare( const_RANDOM ) == 0 ) {
        replace = F( _T( "%d" ), rand() & 0xffff );
      } else {
        MacrosMap::iterator it;
        if( ( it = m_Macros.find( var ) ) != m_Macros.end() ) {
          replace = it->second;
        }
      }
    }
    const wxChar l = search.Last();
    if( l == _T( '/' ) || l == _T( '\\' ) || l == _T( '$' ) || l == _T( ' ' ) ) {
      replace.append( l );
    }
    if( replace.IsEmpty() ) {
      wxGetEnv( var, &replace );
    }
    buffer.Replace( search, replace, false );
  }
  while( m_RE_DOS.Matches( buffer ) ) {
    replace.Empty();
    search = m_RE_DOS.GetMatch( buffer, 2 );
    wxString var = m_RE_DOS.GetMatch( buffer, 3 ).Upper();
    if( var.GetChar( 0 ) == _T( '#' ) ) {
      replace = UnixFilename( m_UserVarMan->Replace( var ) );
    } else {
      if( var.compare( const_COIN ) == 0 ) {
        replace.assign( 1u, rand() & 1 ? _T( '1' ) : _T( '0' ) );
      } else if( var.compare( const_RANDOM ) == 0 ) {
        replace = F( _T( "%d" ), rand() & 0xffff );
      } else {
        MacrosMap::iterator it;
        if( ( it = m_Macros.find( var ) ) != m_Macros.end() ) {
          replace = it->second;
        }
      }
    }
    if( replace.IsEmpty() ) {
      wxGetEnv( var, &replace );
    }
    buffer.Replace( search, replace, false );
  }
  if( !subrequest ) {
    buffer.Replace( _T( "%%" ), _T( "%" ) );
    buffer.Replace( _T( "$$" ), _T( "$" ) );
  }
}

wxString MacrosManager::EvalCondition( const wxString& in_cond, const wxString& true_clause, const wxString& false_clause, ProjectBuildTarget* target ) {
  enum condition_codes {EQ = 1, LT = 2, GT = 4, NE = 8};
  wxString cond( in_cond );
  wxString result;
  ReplaceMacros( cond, target, true );
  if( !m_RE_IfSp.Matches( in_cond ) ) {
    return false_clause;
  }
  wxString cmpToken( m_RE_IfSp.GetMatch( in_cond, 3 ).Strip( wxString::both ) );
  wxString left( m_RE_IfSp.GetMatch( in_cond, 2 ).Strip( wxString::both ) );
  wxString right( m_RE_IfSp.GetMatch( in_cond, 4 ).Strip( wxString::both ) );
  int compare = left.Cmp( right );
  if( compare == 0 ) {
    compare = EQ;
  } else if( compare < 0 ) {
    compare = LT | NE;
  } else if( compare > 0 ) {
    compare = GT | NE;
  }
  if( cmpToken.IsEmpty() ) {
    if( cond.IsEmpty() || cond.IsSameAs( _T( "0" ) ) || cond.IsSameAs( _T( "false" ) ) ) {
      return false_clause;
    }
    return true_clause;
  }
  int condCode = 0;
  if( cmpToken.IsSameAs( _T( "==" ) ) || cmpToken.IsSameAs( _T( "=" ) ) ) {
    condCode = EQ;
  }
  if( cmpToken.IsSameAs( _T( "<" ) ) ) {
    condCode = LT;
  }
  if( cmpToken.IsSameAs( _T( ">" ) ) ) {
    condCode = GT;
  }
  if( cmpToken.IsSameAs( _T( "<=" ) ) ) {
    condCode = EQ | LT;
  }
  if( cmpToken.IsSameAs( _T( ">=" ) ) ) {
    condCode = EQ | GT;
  }
  if( cmpToken.IsSameAs( _T( "!=" ) ) ) {
    condCode = NE;
  }
  return condCode & compare ? true_clause : false_clause;
}

int MacrosManager::MatchBrace( const wxString& buffer, int index ) {
  int depth = 0;
  while( index < ( int )buffer.Length() ) {
    if( buffer[index] == _T( '{' ) ) {
      ++depth;
    } else if( buffer[index] == _T( '}' ) ) {
      --depth;
    }
    if( depth == 0 ) {
      break;
    }
    ++index;
  }
  return index;
}
